//
//  ShareController.m
//  SolarSeek
//
//  Created by Erick Fejta on 05/01/27.
//  Copyright 2005 Erick Fejta. All rights reserved.
//

#import "ShareController.h"
#import "Shares.h"
#import "Message.h"
#import "SelfFillingMutables.h"
#import "Peers.h"
#import "PeerConnectionsController.h"
#import "Connections.h"
#import "SolarSeekEngine.h"
#import "ServerConnectionController.h"
#import "TransfersController.h"


@interface Peer (Shares)
- (void)addShare:(Share *)share;
- (void)addShares:(id)shares;
@end

@implementation Peer (Shares)
- (void)addShare:(Share *)share {
	[self addShares:[NSArray arrayWithObject:share]];
}

- (void)addShares:(id)shares {
//	[self performSelectorOnMainThread:@selector(willChangeValueForKey:)
//		withObject:@"countFiles" waitUntilDone:NO];
	[self willChangeValueForKey:@"countFiles"];
	NSEnumerator *e = [shares objectEnumerator];
	Share *s;
	while(s = [e nextObject])
		[sharesTree addShare:s];
	[self didChangeValueForKey:@"countFiles"];
//	[self performSelectorOnMainThread:@selector(didChangeValueForKey:) 
//			withObject:@"countFiles" waitUntilDone:NO];
}

@end

@class ControlledSharePeer;

@interface ShareController (Control) 
- (ControlledSharePeer *)sharePeerForDecodedSharePeer:(ControlledSharePeer *)controlledSharePeer;
- (void)removeSharedPeer:(ControlledSharePeer *)sharePeer;
@end


@interface ControlledSharePeer : SharePeer {}
@end

@implementation ControlledSharePeer 

- (void)release {
	if([self retainCount]==2) {
		[super release];
		[[[NSApp engine] shareController] removeSharedPeer:self];
	}
	else
		[super release];
}

- (id)initWithCoder:(NSCoder *)coder {
	return [super initWithCoder:coder];
}

- (void)encodeWithCoder:(NSCoder *)coder {
	[super encodeWithCoder:coder];
}

unsigned countt = 0;
float totalelapsed = 0;

- (id)awakeAfterUsingCoder:(NSCoder *)coder {
	NSDate *d = [[NSDate alloc] init];
	ControlledSharePeer *other = (ControlledSharePeer *)[[[NSApp engine] shareController] sharePeerForDecodedSharePeer:self];
	if(other != self) {
		[other retain];
		[self release];
		self = other;
	}
	countt++;
	float elapsed = -[d timeIntervalSinceNow];
	totalelapsed += elapsed;
//	if(rand()%1000 == 0)
//		NSLog(@"%d done. %.5f elapsed awaking %@. Averaging %f seconds per peer (%f/%d) ",countt,elapsed,[self filenameSharedUnix],totalelapsed/(float)countt,totalelapsed,countt);
	[d release];
	return self;
}

@end

#pragma mark -


@interface SearchShareController : NSObject {
	NSSet *_shares;
}

- (id)initShareControllerWithSet:(NSSet *)shares;
@end

BOOL TouchedShareController = NO;

@implementation SearchShareController

- (id)initShareControllerWithSet:(NSSet *)shares {
	self = [super init];
	if(self) {
		_shares = shares;
	}
	return self;
}

- (id)valueForKey:(NSString *)key {
	NSEnumerator *e = [_shares objectEnumerator];
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:50];
	ShareLocal *o;
	while(o = [e nextObject]) {
		NSRange r = [[o filenameLocal] rangeOfString:key options:NSCaseInsensitiveSearch];
		if(r.location != NSNotFound) 
			[a addObject:o];
	}
	return a;
}

@end




#pragma mark -

int sortNumericCaseInsensitive(id a, id b, void *context) { return [a compare:b options:NSCaseInsensitiveSearch|NSNumericSearch]; }

@interface ShareController (Private) 
- (void)addShares:(NSSet *)shares toResult:(Message *)message;
- (NSArray *)arrangeSharedFilesIntoDirectoriesWithFiles:(NSMutableDictionary **)files forPeerConnection:(PeerConnection *)peerConnection;
- (void)changeWithEnumerator:(NSEnumerator *)files directory:(NSString *)dir selector:(SEL)action ;
- (void)commitAddAndWait:(BOOL)wait;
- (void)readShareFromMessage:(Message *)message fromPeer:(Peer *)peer andToken:(unsigned)token;
- (void)readResultsOfMessage:(Message *)message fromPeerConnection:(PeerConnection *)peerConnection;
- (void)readSharesWithCode:(int)code fromMessage:(Message *)message peerConnection:(PeerConnection *)peerConnection;
- (void)readSearchResults:(Message *)message fromPeerConnection:(PeerConnection *)peerConnection;
- (void)sendSearchResultsToPeerConnection:(PeerConnection *)peerConnection forToken:(unsigned)token query:(NSString *)query;
- (void)sendShareListToPeer:(Peer *)peer;
- (void)setContentsFolderReceived:(NSString *)folder; 
@end

@implementation ShareController
- (void)schedulePoll {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(poll) object:nil];
	[self performSelector:@selector(poll) withObject:nil afterDelay:10.0];
}

- (void)poll {
	if(TouchedShareController) {
		@synchronized (_sharesPeerReceivedTemp) {
			if(TouchedShareController) {
				TouchedShareController = NO;
				[self commitAddAndWait:NO];
			}
		}
	}
	[self schedulePoll];
}

- (id)init {
	[NSException raise:@"InvalidArgumentException" format:@"[ShareController init] is not a valid initializer."];
	[self release];
	return nil;
}

void *ObserveLogIn = (void *)1954;

- (id)initWithEngine:(SolarSeekEngine *)engine {
	self = [super init];
	if (self) {
		_contentsFolderReceived = nil;
		_engine = engine;
		_shares = [[NSMutableSet alloc] initWithCapacity:10000];
		_sharesPeer = [[NSMutableSet alloc] initWithCapacity:100000];
		_sharesPeerReceived = [[NSArray alloc] init];
		_sharesPeerReceivedTemp = [[NSMutableArray alloc] init];
		_fm = [NSFileManager defaultManager];
		_serverConnection = [[engine serverConnection] retain];
		[[engine serverConnectionController] addObserver:self forKeyPath:@"loggedIn" options:nil context:ObserveLogIn];
			[_serverConnection addObserver:self forKeyPath:@"messageReceived" options:nil context:nil];
		_peerConnectionsController = [[engine peerConnectionsController] retain];
			[_peerConnectionsController addObserver:self forKeyPath:@"peerConnectionWithNewMessage" options:nil context:nil];
		[self poll];
	}
	return self;
}

- (void)dealloc {
	[_sharesPeerReceived release];
	[_sharesPeerReceivedTemp release];
	[_sharesPeer release];
	[[[NSApp engine] serverConnectionController] removeObserver:self forKeyPath:@"loggedIn"];
	[_shares release];
		[_serverConnection removeObserver:self forKeyPath:@"messageReceived"];		//fejta: TODO: low: make sure we remove all our observing.
	[_serverConnection release];
		[_peerConnectionsController removeObserver:self forKeyPath:@"peerConnectionWithNewMessage"];
	[_peerConnectionsController release];
	[super dealloc];
}

#pragma mark actions

- (void)addDirectoryExcludingSubdirectories:(NSString *)directory {
	[self changeWithEnumerator:[[_fm directoryContentsAtPath:directory] objectEnumerator] directory:directory selector:@selector(addObject:)];
}

- (void)addFile:(NSString *)file {
	[NSApp appendLogWithHeader:@"SHARES"
		HTML:[@"Adding share at " stringByAppendingString:file] 
		type:LogTypeShare];
	file = [file stringByStandardizingPath];
	id sl = [[ShareLocal alloc] initWithFile:file privilege:0];
	[_shares addObject:sl];
	[sl release];
	[self performSelectorOnMainThread:@selector(updateShares) withObject:nil waitUntilDone:NO];
}
- (void)addDirectory:(NSString *)directory {
	[NSThread detachNewThreadSelector:@selector(addDirectoryInBackground:) toTarget:self withObject:directory];
}

- (void)addDirectoryInBackground:(NSString *)directory {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[NSThread setThreadPriority:0.1];
	[NSApp appendLogWithHeader:@"SHARES"
		HTML:[@"Adding shares from " stringByAppendingString:directory] 
		type:LogTypeShare];
	directory = [directory stringByStandardizingPath];
	[self changeWithEnumerator:[_fm enumeratorAtPath:directory] directory:directory selector:@selector(addObject:)];
	[pool release];
}


- (NSArray *)arrangeSharedFilesIntoDirectoriesWithFiles:(NSMutableDictionary **)files forPeer:(Peer *)peer {
	*files = [NSMutableDictionary dictionaryWithCapacity:[_shares count]/20];
	NSMutableSet *dirSet = [NSMutableSet set];
	NSEnumerator *e = [_shares objectEnumerator];
	ShareLocal *s;
	while(s = [e nextObject]) {
		if([s floorPrivileges] > [peer privileges])
			continue;
		NSString *dir = [ShareLocal convertFilenameToStyleShared:[[s filenameSharedUnconverted] stringByDeletingLastPathComponent]];
		unsigned count = [dirSet count];
		[dirSet addObject:dir];
		if(files) {
			if(count != [dirSet count])
				[*files setObject:[NSMutableSet set] forKey:dir];
			NSMutableSet *filesForDir = [*files objectForKey:dir];
			[filesForDir addObject:s];
		}
	}
	return [[dirSet allObjects] sortedArrayUsingFunction:sortNumericCaseInsensitive context:nil];
}

- (void)addShares:(NSSet *)shares toResult:(Message *)message sendDirectory:(BOOL)includedir{
	[message putInt:[shares count]];
	NSEnumerator *e = [shares objectEnumerator];
	ShareLocal *share;
	while(share = [e nextObject]) {
		[message putByte:1]; //fejta: TODO: medium why the byte here??
		NSString *sharename = includedir?[share filenameShared]:[[share filenameSharedUnconverted] lastPathComponent];
		[message putString:sharename];
		[message putLongLong:[share size]];
		[message putString:[sharename pathExtension]];
		[message putInt:0]; //num of attributes
	}
}


- (void)addFileOrDirectory:(NSString *)path {
	path = [path stringByStandardizingPath];
	BOOL dir=NO;
	BOOL exists;
	if(exists = [_fm fileExistsAtPath:path isDirectory:&dir] && dir) {
		[self addDirectory:path];
	}
	else if([_fm fileExistsAtPath:path])
		[self addFile:path];
}

- (void)changeWithEnumerator:(NSEnumerator *)files directory:(NSString *)dir selector:(SEL)action {
	NSString *o;
	int count=0;
	NSDate *start = [[NSDate alloc] init];
	BOOL isDir;
	while(o = [files nextObject]) {
		o = [dir stringByAppendingPathComponent:o];
		if([_fm fileExistsAtPath:o isDirectory:&isDir] && isDir == NO) {
			count++;
			id sl = [[ShareLocal alloc] initWithFile:o privilege:0];
			[_shares performSelector:action withObject:sl];
			[sl release];
		}
	}
	NSString *str = [NSString stringWithFormat:@"%d files changed.  %f seconds elapsed.",count,[start timeIntervalSinceNow]*-1];
	[NSApp appendLogWithHeader:@"SHARES" HTML:str type:LogTypeShare];
	[start release];
	[self performSelectorOnMainThread:@selector(updateShares) withObject:nil waitUntilDone:NO];
}

- (void)addInBackground {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	@try {
		[self willChangeValueForKey:@"sharesPeerReceived"];
		@try {
			@synchronized(_sharesPeerReceivedTemp) {
				[_sharesPeerReceived release];
				_sharesPeerReceived = [[NSArray arrayWithArray:_sharesPeerReceivedTemp] retain];
				[_sharesPeerReceivedTemp removeAllObjects];
			}
			NSEnumerator *e = [_sharesPeerReceived objectEnumerator];
			SharePeer *sp;
			NSMutableArray *funbuf = [[NSMutableArray alloc] init];
			while(sp = [e nextObject]) {
				[funbuf removeAllObjects];
				Peer *p = [sp peer];
				do {
					if(p==[sp peer])
						[funbuf addObject:sp];
					else
						break;
				} while(sp = [e nextObject]);
				
				[p addShares:funbuf];			
				if(sp) 
					[[sp peer] addShare:sp];
			}
			[funbuf release];
		}
		@finally {
			[self didChangeValueForKey:@"sharesPeerReceived"];
		}
	}
	@finally{
		[pool release];
	}
}


- (void)add {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(add) object:nil];
	@synchronized(_sharesPeerReceivedTemp) {
		if([_sharesPeerReceivedTemp count] > 100) {
			[NSThread detachNewThreadSelector:@selector(addInBackground) toTarget:self withObject:nil];
		}
		else
			[self addInBackground];
	}
}




- (void)scheduleAdd {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(add) object:nil];
	[self performSelector:@selector(add) withObject:nil afterDelay:0.08];
}

- (void)commitAddAndWait:(BOOL)wait {
	[self performSelectorOnMainThread:@selector(scheduleAdd) withObject:nil waitUntilDone:wait];
}

- (void)addShare:(ControlledSharePeer *)controlledSharePeer {
	@synchronized(_sharesPeerReceivedTemp) {
		[_sharesPeerReceivedTemp addObject:controlledSharePeer];
		TouchedShareController = YES;
	}
}

- (void)readShareFromMessage:(Message *)message subdirectory:(NSData *)subdir peer:(Peer *)peer token:(unsigned)token{
	char code = [message getByte];
	NSData *filename =  [message getData];
	if(subdir) {
		NSMutableData *md = [NSMutableData dataWithData:subdir];
		[md appendData:[@"\\" dataUsingEncoding:[NSString defaultCStringEncoding]]];
		[md appendData:filename];
		filename = md;
	}
	unsigned long long size = [message getLongLong];
	NSString *ext = [message getString];
	unsigned attributes = [message getInt];
	unsigned m = attributes;
	//if attributes==3 then 0=(int)bitrate 1=(int)length 2=(BOOL)vbr
	//also witnessed attributes==4
	unsigned bitrate=NSNotFound,
			 length=NSNotFound;
	BOOL	 vbr=NO;
	while(m--) {
		unsigned type = [message getInt];
		unsigned value = [message getInt];
		switch(type) {
		case 0: //bitrate
			bitrate = value;
			break;
		case 1:	//length
			length = value;
			break;
		case 2:	//is VBR?
			vbr = value!=0;
			break;
		default:
			NSLog(@"Unknown result type: %d with value %d",type,value);
		}
	}
	NSMutableDictionary *atts = [NSMutableDictionary dictionaryWithObjects:
		[NSArray arrayWithObjects:filename,[NSNumber numberWithUnsignedLongLong:size],ext,[NSNumber numberWithChar:code],[NSNumber numberWithUnsignedInt:token],nil]
		forKeys:[NSArray arrayWithObjects:@"filename",NSFileSize,@"extension",@"code",@"token",nil]];
	if(bitrate != NSNotFound)
		[atts setObject:[NSNumber numberWithUnsignedInt:bitrate] forKey:@"bitrate"];
	if(length != NSNotFound)
		[atts setObject:[NSNumber numberWithUnsignedInt:length] forKey:@"length"];
	[atts setObject:[NSNumber numberWithBool:vbr] forKey:@"VBR"];
	id csp = [[ControlledSharePeer alloc] initWithPeer:peer filenameData:filename andAttributes:atts];
	[self addShare:csp];
	[csp release];
}

- (void)readShareFromMessage:(Message *)message peer:(Peer *)peer token:(unsigned)token{
	[self readShareFromMessage:message subdirectory:nil peer:peer token:token];
}


- (void)readSearchResults:(Message *)message fromPeerConnection:(PeerConnection *)peerConnection {
	if([message decompress]) {
		NSString *name = [message getString];
		if([name isEqual:[[peerConnection peer] name]] == NO) {
			[NSApp appendLogWithHeader:@"RESULTS" 
			  HTML:[NSString stringWithFormat:@"%@ sent a file search result with %@ as the name.",[[peerConnection peer] name],name]
			  type:LogTypeError|LogTypeSearch|LogTypeLoud];
			name = [[peerConnection peer] name];
		}
		unsigned token = [message getInt];
		unsigned filesnum = [message getInt];
		unsigned n = filesnum;
		Peer *p = [peerConnection peer];
		while(n--) {
			[self readShareFromMessage:message peer:p token:token];
		}
		[NSApp appendLogWithHeader:@"RESULTS" 
				HTML:[NSString stringWithFormat:@"%@ sent us %d results for search numbered %d.",[peerConnection peer],filesnum,token]
				type:LogTypeSearch];
	}
}

- (void)readSharesWithCode:(int)code fromMessage:(Message *)message peerConnection:(PeerConnection *)peerConnection {
	if([message decompress]) {
		Peer *p = [peerConnection peer];
		NSMutableData *rootname=nil;
		int token = 0;
		unsigned length = 0;
		if(code==FolderContentsResponse) {
			token = [message getInt];
			rootname = [NSMutableData dataWithData:[message getData]];
			[rootname appendData:[@"\\" dataUsingEncoding:[NSString defaultCStringEncoding]]];
			length = [rootname length];
		}
		else if(code == SharedFileList) {
			[NSApp appendLogWithHeader:@"BROWSE"
			  HTML:[[[peerConnection peer] name] stringByAppendingString:@" sent his or her share list."] 
			  type:LogTypeShare|LogTypePeer];
			token = [p token];
		}
		
		unsigned count = [message getInt];
		unsigned i=count;
		while(i-- > 0) {
			NSData *dirname = [message getData];
			if(rootname) {
				NSMutableData *d = [[[NSMutableData alloc] initWithData:rootname] autorelease];
				[d appendData:dirname];
				dirname = d;
			}
			unsigned files = [message getInt];
			unsigned j = files;
			while(j--) {
				[self readShareFromMessage:message subdirectory:dirname peer:p token:token];
			}
		}
		
		if(rootname) {
			[rootname setLength:length - 1];
			NSString *folder = [Message stringFromData:rootname];
			if(folder)
				[self setContentsFolderReceived:folder];
		}
	}
	else
		[NSException raise:@"DecompressException" format:@"Could not decompress directory result message from %@.",[peerConnection peer]];
}

- (void)removeSharedPeer:(ControlledSharePeer *)sharePeer {
	[_sharesPeer removeObject:sharePeer];
}


- (void)removeFileOrDirectory:(NSString *)path {
	path = [path stringByStandardizingPath];
	BOOL dir=NO;
	if([_fm fileExistsAtPath:path isDirectory:&dir] && dir) {
		[self removeDirectory:path];
	}
	else
		[self removeFile:path];
}		


- (void)removeDirectory:(NSString *)directory {
	[NSThread detachNewThreadSelector:@selector(removeDirectoryInBackground:) toTarget:self withObject:directory];
}

- (void)removeDirectoryInBackground:(NSString *)directory {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[NSThread setThreadPriority:0.1];
	[NSApp appendLogWithHeader:@"SHARES"
		HTML:[@"Removing shares from " stringByAppendingString:directory] 
		type:LogTypeShare];
	directory = [directory stringByStandardizingPath];
	[self changeWithEnumerator:[_fm enumeratorAtPath:directory] directory:directory selector:@selector(removeObject:)];
	[pool release];
}

		
- (void)removeDirectoryExcludingSubdirectories:(NSString *)directory {
	[self changeWithEnumerator:[[_fm directoryContentsAtPath:directory] objectEnumerator] directory:directory selector:@selector(removeObject:)];
}

- (void)removeFile:(NSString *)file {
	[NSApp appendLogWithHeader:@"SHARES"
		HTML:[@"Removing share at " stringByAppendingString:file] 
		type:LogTypeShare];
	file = [file stringByStandardizingPath];
	ShareLocal *sl = [[ShareLocal alloc] initWithFile:file privilege:0];
	[_shares removeObject:sl];
	[sl release];
	[self performSelectorOnMainThread:@selector(updateShares) withObject:nil waitUntilDone:NO];
}


- (void)sendShareListToPeer:(Peer *)peer {
	//first we get an array of directories with a dictionary containing the array of files for each directory.
	NSMutableDictionary *files;
	NSArray *dirs = [self arrangeSharedFilesIntoDirectoriesWithFiles:&files forPeer:peer];
	Message *m = [Message messageWithCode:SharedFileList];
	[m compress];
	[m putInt:[dirs count]];	
	NSEnumerator *e = [dirs objectEnumerator]; //go through each returned dir
	NSString *dir;
	while(dir = [e nextObject]) {
		[m putString:dir];
		NSSet *filesfordir = [files objectForKey:dir];
		[self addShares:filesfordir toResult:m sendDirectory:NO];
	}
	
	
	PeerConnection *pc = [[[NSApp engine] peerConnectionsController] peerConnectionForPeer:peer];
//	NSLog(@"Sent shared file list to %@",peer);

	[m finish];
	
	[pc sendMessage:m];
	
	
	//code
	//COMPRESS
	//dircount
	//	dir
	//	filecount
	//		file code? (byte??)
	//		filename (string)
	//		size (long long)
	//		extension (string)
	//		attribute count
	//			attribute type
	//			attribute value.
}

- (void)sendSearchResultsToPeerConnection:(PeerConnection *)peerConnection forToken:(unsigned)token query:(NSString *)query {
	NSDate *begin = [NSDate date];
	NSArray *a = [query componentsSeparatedByString:@" "];
	if([a count]==0)
		return;
	
	NSMutableSet *results  = [NSMutableSet set];
	ShareLocal *l;
	NSEnumerator *e = [_shares objectEnumerator];	
	while(l = [e nextObject]) {
		if([[peerConnection peer] privileges] < [l floorPrivileges])
			continue;
		NSEnumerator *words = [a objectEnumerator];
		NSString *word;
		NSString *name = [l filenameShared];
		while(word = [words nextObject]) {
			NSRange r;
			if([name rangeOfString:word options:NSCaseInsensitiveSearch].location == NSNotFound)
				break;
		}
		if(word==nil)
			[results addObject:l];
	}
	
	if([results count]) {
		Message *m = [Message messageWithCode:FileSearchResult];
		[m compress];
		[m putString:[[_engine serverConnectionController] username]];
		[m putInt:token];
		[self addShares:results toResult:m sendDirectory:YES];
		//fejta: TODO: medium: I bet this info is wrong...
		unsigned allowed = [[[NSApp engine] transfersController] countTransfersAllowed];
		unsigned cur = [[[NSApp engine] transfersController] countTransfersCurrent];
		[m putByte:allowed-cur];	//free upload slots...
		[m putInt:0];	//upload speed.
		[m putInt:[[[NSApp engine] transfersController] countTransfersQueued]];	//in queue...?
		[m finish];
		[peerConnection sendMessage:m];
		NSDate *end = [NSDate date];
		[NSApp appendLogWithHeader:@"SEARCH" 
				HTML:[NSString stringWithFormat:@"%@ searched for %@.  %d results found.",[peerConnection peer],query,[results count]] 
				type:LogTypeSearch];
	}
}



- (void)updateShares {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(detatchUpdateSharesThread) object:nil];
	[self performSelector:@selector(detatchUpdateSharesThread) withObject:nil afterDelay:0.2];
}

- (void)detatchUpdateSharesThread {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(detatchUpdateSharesThread) object:nil];
	[NSThread detachNewThreadSelector:@selector(updateSharesInBackground) toTarget:self withObject:nil];
}

- (void)sendShareInfo {
	if([[[NSApp engine] serverConnectionController] loggedIn]) {
		Message *m = [Message messageWithCode:SharedFoldersFiles];
		unsigned files = [[[[NSApp engine] me] sharesTreeRoot] countSharesThisAndSubLevels];
		unsigned folders = [[[[NSApp engine] me] sharesTreeRoot] countTreesFilledThisAndSubLevels];
		[m putInt:folders];
		[m putInt:files];
		[[NSApp engine] sendServerMessage:m];
		[NSApp appendLogWithHeader:@"SHARES" HTML:[NSString stringWithFormat:@"Telling server we are sharing %d files and %d directories",files,folders] type:LogTypeShare];
	}
}

- (void)updateSharesInBackground {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	@try {
	@synchronized([NSApp engine]) {
		[self willChangeValueForKey:@"countFilesShared"];
		[self willChangeValueForKey:@"filesAllShared"];
		[[[NSApp engine] me] addShares:[_shares allObjects]];
		[NSApp appendLogWithHeader:@"SHARES"
			HTML:[NSString stringWithFormat:@"Now sharing %d files.",
					[[[[NSApp engine] me] sharesTreeRoot] countSharesThisAndSubLevels]]
			type:LogTypeShare|LogTypeLoud];

		[self didChangeValueForKey:@"filesAllShared"];
		[self didChangeValueForKey:@"countFilesShared"];
		[self sendShareInfo];
	}
	}
	@catch(id ue) {
		NSLog([ue description]);
	}
	[pool release];
}




#pragma mark KVO/C

- (NSString *)contentsFolderReceived { return _contentsFolderReceived; }

- (unsigned)countFilesShared {
	return [_shares count];
}

- (NSArray *)filesAllShared {
	return [_shares allObjects];
}

- (id)searchForWords {
	return [[[SearchShareController alloc] initShareControllerWithSet:_shares] autorelease];
}

- (ControlledSharePeer *)sharePeerForDecodedSharePeer:(ControlledSharePeer *)controlledSharePeer {
	ControlledSharePeer *csp = [_sharesPeer member:controlledSharePeer];
	if(csp == nil) {
		csp = controlledSharePeer;
		[_sharesPeer addObject:csp];
	}
	
	[self addShare:csp];
	return csp;
}
	
- (ShareLocal *)shareForSharedPath:(NSString *)sharedPath {
	//fejta: TODO: FIX THIS using trees!!!
	NSEnumerator *e = [_shares objectEnumerator];
	ShareLocal *local;
	while(local = [e nextObject]) {
		if([[local filenameShared] isEqual:sharedPath])
			return local;
	}
	return nil;
}

- (void)setContentsFolderReceived:(NSString *)folder {
	[_contentsFolderReceived release];
	_contentsFolderReceived = [folder retain];
}

- (NSArray *)sharesPeerReceived {return _sharesPeerReceived; }

#pragma mark observing

- (void)processPeerMessageInBackground:(NSArray *)array {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//	[NSThread setThreadPriority:0.1];
	Message *m = [array objectAtIndex:0];
	Peer *p = [[NSApp engine] peerNamed:[array objectAtIndex:1]];
	@synchronized(p) {
		PeerConnection *pc = [[[NSApp engine] peerConnectionsController] peerConnectionForPeer:p];
		[m rewind];
		unsigned code = [m getCode];
		switch(code) {
			case PeerFileSearch:
//				[NSThread setThreadPriority:0.05];
				[self sendSearchResultsToPeerConnection:pc forToken:[m getInt] query:[m getString]];
				break;
				
			case FileSearchResult:
				[self readSearchResults:m fromPeerConnection:pc];
				break;
			case FolderContentsResponse:
			case SharedFileList:
				[self readSharesWithCode:code fromMessage:m peerConnection:pc];
				break;
			case GetSharedFileList:
				[self sendShareListToPeer:[pc peer]];
				break;
		}
	}
	[self commitAddAndWait:YES];
	[array release];
	[pool release];
}
	

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
	if(context == ObserveLogIn) {
		[self sendShareInfo];
	}
	else if(object == _peerConnectionsController && [keyPath isEqual:@"peerConnectionWithNewMessage"]) {
		//recevied a peer message.
		PeerConnection *pc = [_peerConnectionsController peerConnectionWithNewMessage];
		Message *m = [pc messageReceived];
		[m rewind];
		unsigned code = [m getCode];
		switch(code) {
			case PeerFileSearch:
			case FileSearchResult:
			case FolderContentsResponse:
			case SharedFileList:
			case GetSharedFileList: {
				//thread will release array.
				NSArray *array = [[NSArray alloc] initWithObjects:m,[[pc peer] name],nil];
				[self processPeerMessageInBackground:array];
//				[NSThread detachNewThreadSelector:@selector(processPeerMessageInBackground:) toTarget:self withObject:array];
				break;
			}
		}
	}
	else if(object == _serverConnection && [keyPath isEqual:@"messageReceived"]) {
		//received a server message.
		Message *m = [_serverConnection messageReceived];
		[m rewind];
		unsigned code = [m getCode];
		switch(code) {
			case FileSearch: {
				NSString *username = [m getString];
				unsigned token = [m getInt];
				NSString *fun = [m getString];
				[self sendSearchResultsToPeerConnection:[_peerConnectionsController peerConnectionForName:username] forToken:token query:fun];
				break;
			}
			case ExactFileSearch:
				NSLog(@"TODO: low: exact file search.");
				break;
		}
	}
	else
		[NSException raise:@"UnknownKeyValueObservedException" format:@"ShareController cannnot respond to keyPath: %@ for object: %@.",keyPath, object];
}


@end
